const { Writable } = require('stream')
const fs = require('node:fs')

// 创建一个继承自Writable的类FileWriteStream
class FileWriteStream extends Writable {
  // 构造函数，接收一个对象作为参数
  constructor({ highWaterMark, fileName }) {
    // 调用父类的构造函数
    super({ highWaterMark })

    // 保存文件名
    this.fileName = fileName
    // 文件描述符
    this.fd = null
    // 缓存块
    this.chunks = []
    // 缓存块大小
    this.chunkSize = 0
    // 写入次数
    this.writesCount = 0
  }

  // 构造函数，打开文件
  _construct(callback) {
    // 打开文件
    fs.open(this.fileName, 'w', (err, fd) => {
      // 如果打开文件出错，则返回错误
      if (err) {
        return callback(err)
      }

      // 保存文件描述符
      this.fd = fd
      // 调用回调函数
      callback()
    })
  }

  // 写入数据
  _write(chunk, encoding, callback) {
    // 将数据块加入缓存
    this.chunks.push(chunk)
    // 更新缓存块大小
    this.chunkSize += chunk.length

    // 如果缓存块大小大于等于highWaterMark，则写入文件
    if (this.chunkSize >= this.highWaterMark) {
      // 将缓存块写入文件
      fs.write(this.id, Buffer.concat(this.chunks), (err) => {
        // 如果写入文件出错，则返回错误
        if (err) {
          return callback(err)
        }

        // 清空缓存块和缓存块大小
        this.chunks = []
        this.chunkSize = 0
        // 写入次数加1
        ++this.writesCount
        // 调用回调函数
        callback()
      })
    } else {
      // 如果缓存块大小小于highWaterMark，则直接调用回调函数
      callback()
    }
  }

  // 结束写入
  _final(callback) {
    // 将缓存块写入文件
    fs.write(this.fd, Buffer.concat(this.chunks), (err) => {
      // 如果写入文件出错，则返回错误
      if (err) {
        return callback(err)
      }

      // 写入次数加1
      ++this.writesCount
      // 清空缓存块
      this.chunks = []
      // 调用回调函数
      callback()
    })
  }

  // 销毁
  _destroy(error, callback) {
    // 如果文件描述符存在，则关闭文件
    if (this.fd) {
      fs.close(this.fd, (err) => {
        // 调用回调函数，传入错误
        callback(err || error)
      })
    } else {
      // 如果文件描述符不存在，则直接调用回调函数
      callback(error)
    }
  }
}

;(async () => {
  console.time('writeMany')

  const stream = new FileWriteStream({
    fileName: 'text.txt',
    highWaterMark: 1800,
  })

  let i = 0

  const numberOfWrites = 1000000

  const writeMany = () => {
    while (i < numberOfWrites) {
      const buff = Buffer.from(` ${i} `, 'utf-8')

      // this is our last write
      if (i === numberOfWrites - 1) {
        return stream.end(buff)
      }

      // if stream.write returns false, stop the loop
      if (!stream.write(buff)) break

      i++
    }
  }

  writeMany()

  let d = 0
  // resume our loop once our stream's internal buffer is emptied
  stream.on('drain', () => {
    ++d
    writeMany()
  })

  stream.on('finish', () => {
    console.log('Number of drains:', d)
    console.timeEnd('writeMany')
  })
})()
